#include <iostream>
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
#include <DagMtx.h>
#include <Defines.h>
#include <Statistics.h>
using namespace std;
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
void ArrStatistics::WriteToFile( FILE * FH ) const {
   double avgBytesPerElem = double( numBytes ) / double( numElems );
   fprintf( FH, "# numElems: %d\n", numElems );
   fprintf( FH, "# numBytes: %d\n", numBytes );
   fprintf( FH, "# avgBytesPerElem: %.1f\n", avgBytesPerElem );
}
void DagStatistics::WriteToFile( FILE * FH ) const {
   
   assert( virtNodes.size() == uniqNodes.size() );
   
   const U32 nodeSize  = 4 * deg;
   const U32 numNodes  = TotalNodeCount();
   const U32 numBytes  = nodeSize * numNodes;
   const U32 leafBytes = U32( leafSize * double( totLeafElems ) );
   
   double compaction = double( leafBytes ) / double( numBytes );
   
   fprintf( FH, "# numNodes: %d\n",   numNodes  );
   fprintf( FH, "# nodeSize: %d\n",   nodeSize  );
   fprintf( FH, "# leafSize: %.1f\n", leafSize  );
   fprintf( FH, "# numBytes: %d\n",   numBytes  );
   fprintf( FH, "# leafBytes: %d\n",  leafBytes );
   fprintf( FH, "# nnzLeafElems: %d\n", nnzLeafElems );
   fprintf( FH, "# totLeafElems: %d\n", totLeafElems );
   fprintf( FH, "# compaction: %.2fx\n", compaction );
   fprintf( FH, "# leafUniqElems: %d\n", leafUniqElems );
   fprintf( FH, "# leafVirtNodes: %d\n", leafVirtNodes );
   fprintf( FH, "# leafUniqNodes: %d\n", leafUniqNodes );
   
   U32 cumBytes = 0;
   U32 cumNodes = 0;
   
   U32 vnAccum = 0;
   U32 vpAccum = 0;
   // -- overcounts the uniqe path expanded nodes -- U32 uxAccum = 0;
   U32 unAccum = 0;
   
   for( U32 i = 0; i < virtNodes.size(); i++ ) {
      
      U32 vn = virtNodes[i];
      U32 vp = vpceNodes[i];
      // -- overcounts the uniqe path expanded nodes -- U32 ux = upcxNodes[i];
      U32 un = uniqNodes[i];
      
      vnAccum += vn;
      vpAccum += vp;
      // -- overcounts the uniqe path expanded nodes -- uxAccum += ux;
      unAccum += un;
      
      double ratio = double( vn ) / double( un );
      
      cumNodes += un;
      cumBytes += un * nodeSize;
      compaction = double( leafBytes ) / double( cumBytes );
      
      fprintf( FH, "# lvl = %2d, virtNodes = %10d, vpceNodes = %10d, uniqNodes = %10d, cumNodes = %10d, cumBytes = %10d, v:u = %6.1fx, compaction = %6.1fx\n", i, vn, vp, un, cumNodes, cumBytes, ratio, compaction );
   }
   fprintf( FH, "# lvl = --, virtNodes = %10d, vpceNodes = %10d, uniqNodes = %10d\n", vnAccum, vpAccum, unAccum );
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
U32 DagStatistics::TotalNodeCount( void ) const {
   
   U32 nodeCount = 0;
   
   for( U32 i = 0; i < virtNodes.size(); i++ ) {

      nodeCount += uniqNodes[i];
   }
   return nodeCount;
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
void DagStatistics::Audit( void ) const {
   
   assert( leafVirtNodes <= nnzLeafElems  );
   assert(  nnzLeafElems <= totLeafElems  );
   assert( leafUniqNodes <= leafVirtNodes );
   
   assert(  leafUniqNodes <= leafUniqElems );
   
   assert( virtNodes.size() == uniqNodes.size() );
   assert( virtNodes[0] == leafVirtNodes );
   assert( uniqNodes[0] == leafUniqNodes );
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
template < typename node_t > void DagMtx< node_t >::WriteComparitiveStatisticsToFile( FILE * FH ) const {

   assert( dagStats.size() == arrStats.size() );
   
   fprintf( FH, "#\n" );
   
   double ratio;
   
   const U32 deg      = dagStats[0].deg;
   const U32 nodeSize = 4 * deg;
   
   U32 totalDagNodes = 0;
   U32 totalDagBytes = 0;
   U32 totalArrElems = 0;
   U32 totalArrBytes = 0;
   
   for( U32 i = 0; i < dagStats.size(); i++ ) {

      const U32 arrElems = arrStats[i].numElems;
      const U32 arrBytes = arrStats[i].numBytes;
      const U32 numNodes = dagStats[i].TotalNodeCount();
      const U32 dagBytes = nodeSize * numNodes;

      totalDagNodes += numNodes;
      totalArrElems += arrElems;
      totalDagBytes += dagBytes;
      totalArrBytes += arrBytes;
      ratio = double( arrBytes ) / double( dagBytes );
      
      fprintf( FH, "# %s vs. %s\n", dagNames[i].c_str(), arrNames[i].c_str() );
      fprintf( FH, "# arrBytes: %d\n", arrBytes );
      fprintf( FH, "# dagBytes: %d\n", dagBytes );
      fprintf( FH, "# compaction: %.2fx\n", ratio );
      fprintf( FH, "#\n" );
      
   }
   fprintf( FH, "# totalArrElems: %d\n", totalArrElems );
   fprintf( FH, "# totalArrBytes: %d\n", totalArrBytes );
   fprintf( FH, "#\n" );

   ratio = double( totalArrBytes ) / double( totalDagBytes );
   
   if( pathCompacted ) {
      fprintf( FH, "# originalTotalDagNodes: %d\n", totalDagNodes );
      fprintf( FH, "# originalTotalDagBytes: %d\n", totalDagBytes );
      fprintf( FH, "# originalTotalCompaction: %.2fx\n", ratio );
      fprintf( FH, "#\n" );
      
      totalDagNodes = nodes.size();
      totalDagBytes = nodeSize * totalDagNodes;
      double pc_ratio = double( totalArrBytes ) / double( totalDagBytes );
      double pc_advantage = pc_ratio / ratio;
      
      fprintf( FH, "# pathcompTotalDagNodes: %d\n", totalDagNodes );
      fprintf( FH, "# pathcompTotalDagBytes: %d\n", totalDagBytes );
      fprintf( FH, "# pathcompAdvantage: %.2fx\n", pc_advantage   );
      fprintf( FH, "# pathcompTotalCompaction: %.2fx\n", pc_ratio );
      fprintf( FH, "#\n" );
   }
   else {
      fprintf( FH, "# totalDagNodes: %d\n", totalDagNodes );
      fprintf( FH, "# totalDagBytes: %d\n", totalDagBytes );      
      fprintf( FH, "# totalCompaction: %.2fx\n", ratio );
      fprintf( FH, "#\n" );
   }
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// force instantiation of necessary code:
template void DagMtx< BNode >::WriteComparitiveStatisticsToFile( FILE * FH ) const;
template void DagMtx< QNode >::WriteComparitiveStatisticsToFile( FILE * FH ) const;
template void DagMtx< ONode >::WriteComparitiveStatisticsToFile( FILE * FH ) const;
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
ostream & operator << ( ostream & os, const DagStatistics & ds ) {
   
   assert( ds.virtNodes.size() == ds.uniqNodes.size() );

   os << "nnzLeafElems = "    << SETDEC( 7 ) << ds.nnzLeafElems;
   os << ", totLeafElems = "  << SETDEC( 7 ) << ds.totLeafElems;
   os << ", leafUniqElems = " << SETDEC( 7 ) << ds.leafUniqElems;
   os << ", leafUniqNodes = " << SETDEC( 7 ) << ds.leafUniqNodes;
   os << ", leafVirtNodes = " << SETDEC( 7 ) << ds.leafVirtNodes;
   os << endl;
   for( U32 i = 0; i < ds.virtNodes.size(); i++ ) {
      
      U32 un = ds.uniqNodes[i];
      U32 vn = ds.virtNodes[i];
      double ratio = double( vn ) / double( un );
      
      os << "level = "       << SETDEC( 2 ) << i;
      os << ", uniqNodes = " << SETDEC( 7 ) << un;
      os << ", virtNodes = " << SETDEC( 7 ) << vn;
      os << ", ratio = "     << SETFIX( 1 ) << ratio;
      os << "x";
      os << endl;
   }
   return os;
}
